package component

import (
	"account/drivers"
	"account/models"
	"account/server"
	sentry "account/utils/sentryLog"
	"context"
	"encoding/json"
	"fmt"
	"strconv"
	"time"

	"github.com/go-redis/redis"
)

/**
  需求：
    定期的更新redis 中账户信息，确保缓存的账户信息有效
  分析:
    定义周期更新的时间段
	定义获取的数据源
*/

// 定义常量配置
const (
	period                          = 30
	serverNameRedisKey              = "account:account:all"                             // 获取所有的账户信息
	serverNameRedisQueueKey         = "account:acquireAccount:accountInfoQueue"         // 请求的规范：Key
	serverNameRedisQueueCallbackKey = "account:acquireAccount:accountInfoCallbackQueue" // 响应的规范:Key
	expire                          = 0
)

// 抽象接口 | 根据需求封装
type Account interface {
	GetDataFromDB() (accounts []models.Account, err error)
	SetDataToRedis(value string)
	Launch(ctx context.Context)
}

// TODO 定义redis驱动
var redisDb *redis.ClusterClient = drivers.ClientCluster // 集群节点
// var redisDb *redis.Client = drivers.Client // 测试使用单节点

// 定义账户结构体 | 默认集群节点处理 | 测试使用单节点
type AccountTransfer struct {
}

// 初始化
func NewAccountTransfer() *AccountTransfer {
	return &AccountTransfer{}
}

// 获取DB数据
func (at AccountTransfer) GetDataFromDB() (accounts []*models.Account, err error) {
	accounts, err = server.GetAccountAllInfos()
	return
}

// 存入到Redis里面
func (at AccountTransfer) SetDataToRedis(value string) error {
	// 暂时不对写入做异常处理
	_, err := redisDb.Set(serverNameRedisKey, value, expire).Result()
	return err
}

// 定期的更新节点
func (at AccountTransfer) Launch(ctx context.Context) {

	var message string

	for {
		// 初始化周期Timer
		t := time.NewTimer(period * time.Second)

		select {
		case <-ctx.Done():
			return

		case <-t.C:
			// 执行 每30秒
			accounts, err := at.GetDataFromDB()
			if err != nil {
				message = fmt.Sprintf("e : [%s] | [%s]", "从数据库获取账户信息失败。。。。", err.Error())
				sentry.Log.Send(message)
			}

			accountByte, err := json.Marshal(accounts)
			if err != nil {
				message = fmt.Sprintf("e : [%s] | [%s]", "账户信息转换格式失败。。。。", err.Error())
				sentry.Log.Send(message)
			}

			accountStr := string(accountByte)
			if err := at.SetDataToRedis(accountStr); err != nil {
				message = fmt.Sprintf("e : [%s] | [%s]", "账户信息存储到Redis失败。。。。", err.Error())
				sentry.Log.Send(message)
			}
			fmt.Println("测试周期执行")
		}
	}
}

// 定义Account队列处理对象
type AccountQueue struct{}

// 构造
func NewAccountQueueTransfer() *AccountQueue {
	return &AccountQueue{}
}

// 过滤出来需要的数据格式
func (aq AccountQueue) FilterData(query map[string]string, accounts_o []models.Account) (accounts []models.Account) {

	parentId, _ := strconv.Atoi(query["parent_id"])
	for _, account := range accounts_o {

		if account.Market.Code == query["market"] && account.Exchange.Name == query["plat"] && account.ParentId == uint64(parentId) {
			// drivers.SqlClusterDb.
			accounts = append(accounts, account)
		}
	}
	return
}

// 从DB获取数据
func (aq AccountQueue) GetDataFromDB() (accounts []models.Account, err error) {
	accounts, err = server.GetAccountFilterQueueInfos()
	return
}

// 消费队列，并且做返回处理
func (aq AccountQueue) SetDataToRedis(value string) error {
	// 暂时不对写入做异常处理
	_, err := redisDb.LPush(serverNameRedisQueueCallbackKey, value).Result()
	return err
}

// 运行
func (aq AccountQueue) Launch(ctx context.Context) {

	// 	result
	// TODO 如果消息量比较大的话 考虑多并发实现 | 增加消费者

	for {

		var requestMap map[string]string

		select {
		case <-ctx.Done():
			return

		default:

			num, err := redisDb.LLen(serverNameRedisQueueKey).Result()
			if err != nil {
				sentry.Log.Send(fmt.Sprintf("e : [%s] [%s] | info : [%s] ", "获取队列长度异常....", err.Error(), serverNameRedisQueueKey))
			}

			if num > 0 {

				requestData, err := redisDb.BLPop(period*time.Second, serverNameRedisQueueKey).Result()
				if err != nil {
					sentry.Log.Send(fmt.Sprintf("e : [%s] | info : [%s] ", err.Error(), serverNameRedisQueueKey))
				}

				err = json.Unmarshal([]byte(requestData[1]), &requestMap)
				if err != nil {
					sentry.Log.Send(fmt.Sprintf("e : [%s] [%s] | info : [%s] ", "反序列化失败....", err.Error(), requestData[1]))
				}

				accounts, err := aq.GetDataFromDB()
				if err != nil {
					sentry.Log.Send(fmt.Sprintf("e : [%s] | info : [%s] ", err.Error(), "从数据库获取数据失败...."))
				}

				accounts = aq.FilterData(requestMap, accounts)
				accountByte, err := json.Marshal(accounts)
				if err != nil {
					sentry.Log.Send(fmt.Sprintf("e : [%s] [%s] ", "账户信息转换格式失败。。。。", err.Error()))
				}

				accountStr := string(accountByte)
				err = aq.SetDataToRedis(accountStr)
				if err != nil {
					sentry.Log.Send(fmt.Sprintf("e : [%s] | info : [%s] ", err.Error(), "回调失败,写入Redis异常..."))
				}

			}
		}
	}
}
